﻿namespace DAL
{
    /// <summary>
    /// 查询树状数据
    /// </summary>
    public class HighPerfTreeBuilder<TEntity, TNode, TKey> where TKey : notnull
    {
        private readonly Func<TEntity, TNode> _nodeCreator;
        private readonly Func<TEntity, TKey> _getId;
        private readonly Func<TEntity, TKey> _getParentId;
        private readonly Action<TNode, List<TNode>> _setChildrenAction;
        private readonly Func<TEntity, IComparable> _orderBy;
        public HighPerfTreeBuilder(
            Func<TEntity, TNode> nodeCreator,
            Func<TEntity, TKey> getId,
            Func<TEntity, TKey> getParentId,
            Action<TNode, List<TNode>> setChildrenAction,
            Func<TEntity, IComparable> orderBy = null)
        {
            _nodeCreator = nodeCreator;
            _getId = getId;
            _getParentId = getParentId;
            _setChildrenAction = setChildrenAction;
            _orderBy = orderBy ?? (x => 0);
        }

        public List<TNode> BuildTree(IEnumerable<TEntity> entities)
        {
            var entityList = entities.ToList();
            //var entityMap = entityList.ToLookup(e =>
            //    _getParentId(e)?.Equals(default(TKey)) == true ? default : _getParentId(e));
            // 更安全的默认值判断方法
            // 修改后的查询
            var entityMap = entityList.ToLookup(e =>
                IsDefaultKey(_getParentId(e)) ? default : _getParentId(e));

            var roots = entityList.Where(e => IsDefaultKey(_getParentId(e)))
                                 .OrderBy(_orderBy);
           var sdsdsd = roots.Select(e => BuildNode(e, entityMap))
                .ToList();
            return sdsdsd;
        }
        private bool IsDefaultKey(TKey key)
        {
            return EqualityComparer<TKey>.Default.Equals(key, default);
        }

        private TNode BuildNode(TEntity entity, ILookup<TKey, TEntity> entityMap)
        {
            var node = _nodeCreator(entity);
            var children = entityMap[_getId(entity)]
                          .OrderBy(_orderBy)
                          .Select(e => BuildNode(e, entityMap))
                          .ToList();

            _setChildrenAction(node, children);
            return node;
        }
    }
}
